package com.yyfax.pay.common.plugin;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;

import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * SQL查错用，将具体的字体错误打印出来。在批量SQL时比较实用 
 * @author maojian
 * @date 2017-08-10 10:15:41
 * @since v1.0.0
 */
public class MyScriptingLanguage extends XMLLanguageDriver {

	@Override
	public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject,
			BoundSql boundSql) {
		return new MyParameterHandler(mappedStatement, parameterObject, boundSql);
	}

}

/**
 * 将typeHandler.setParameter错误更加清晰的定位
 * @see org.apache.ibatis.scripting.defaults.DefaultParameterHandler
 * @author maojian
 * @date 2017-08-10 14:25:58
 * @since v1.0.0
 */
class MyParameterHandler implements ParameterHandler {
	private final Logger logger = LoggerFactory.getLogger(getClass());

	private final TypeHandlerRegistry typeHandlerRegistry;

	private final MappedStatement mappedStatement;
	private final Object parameterObject;
	private BoundSql boundSql;
	private Configuration configuration;

	public MyParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
		this.mappedStatement = mappedStatement;
		this.configuration = mappedStatement.getConfiguration();
		this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
		this.parameterObject = parameterObject;
		this.boundSql = boundSql;
	}

	@Override
	public Object getParameterObject() {
		return parameterObject;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public void setParameters(PreparedStatement ps) throws SQLException {
		ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
		List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
		if (parameterMappings != null) {
			for (int i = 0; i < parameterMappings.size(); i++) {
				ParameterMapping parameterMapping = parameterMappings.get(i);
				if (parameterMapping.getMode() != ParameterMode.OUT) {
					Object value;
					String propertyName = parameterMapping.getProperty();
					if (boundSql.hasAdditionalParameter(propertyName)) {// issue #448 ask first for additional params
						value = boundSql.getAdditionalParameter(propertyName);
					} else if (parameterObject == null) {
						value = null;
					} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
						value = parameterObject;
					} else {
						MetaObject metaObject = configuration.newMetaObject(parameterObject);
						value = metaObject.getValue(propertyName);
					}
					TypeHandler typeHandler = parameterMapping.getTypeHandler();
					JdbcType jdbcType = parameterMapping.getJdbcType();
					if (value == null && jdbcType == null){
						jdbcType = configuration.getJdbcTypeForNull();
					}
					try {
						typeHandler.setParameter(ps, i + 1, value, jdbcType);
					} catch (Exception e) {
						StringBuilder msg = new StringBuilder();
						msg.append("【出错对象：" + format(boundSql.getParameterObject(),propertyName) + "】\n");
						msg.append("【属性名：" + propertyName + "】\n");
						msg.append(e.getMessage());
						logger.info(msg.toString());
						throw e;
					}
				}
			}
		}
	}
	
	
	
	private String format(Object obj, final String errorColum){
		return new ReflectionToStringBuilder(obj, ToStringStyle.SHORT_PREFIX_STYLE) {
			@Override
			protected void appendFieldsIn(Class<?> clazz) {
		        if (clazz.isArray()) {
		            this.reflectionAppendArray(this.getObject());
		            return;
		        }
		        Field[] fields = clazz.getDeclaredFields();
		        AccessibleObject.setAccessible(fields, true);
		        for (Field field : fields) {
		            String fieldName = field.getName();
		            if (this.accept(field)) {
		                try {
		                    Object fieldValue = this.getValue(field);
		                    if(errorColum.equals(fieldName)){
		                    	fieldName = "就是这个字段("+fieldName+")就是这个字段";
		                    }
		                    this.append(fieldName, fieldValue);
		                } catch (IllegalAccessException ex) {
		                    throw new InternalError("Unexpected IllegalAccessException: " + ex.getMessage());
		                }
		            }
		        }
		    }
		}.toString();
	}

}
